a tool for shared writing and social publishing
at update/reader 197 lines 7.2 kB view raw
1"use client"; 2import { ShortcutKey } from "../../../components/Layout"; 3import { Media } from "../../../components/Media"; 4import { Popover } from "../../../components/Popover"; 5import { metaKey } from "src/utils/metaKey"; 6import { useEntitySetContext } from "../../../components/EntitySetProvider"; 7import { useState } from "react"; 8import { ActionButton } from "components/ActionBar/ActionButton"; 9import { HelpSmall } from "../../../components/Icons/HelpSmall"; 10import { isMac } from "src/utils/isDevice"; 11import { useIsMobile } from "src/hooks/isMobile"; 12 13export const HelpButton = (props: { noShortcuts?: boolean }) => { 14 let entity_set = useEntitySetContext(); 15 let isMobile = useIsMobile(); 16 17 return entity_set.permissions.write ? ( 18 <Popover 19 side={isMobile ? "top" : "right"} 20 align={isMobile ? "center" : "start"} 21 asChild 22 className="max-w-xs w-full" 23 trigger={<ActionButton icon={<HelpSmall />} label="About" />} 24 > 25 <div className="flex flex-col text-sm gap-2 text-secondary"> 26 {/* about links */} 27 <HelpLink text="📖 Leaflet Manual" url="https://about.leaflet.pub" /> 28 <HelpLink text="💡 Make with Leaflet" url="https://make.leaflet.pub" /> 29 <HelpLink 30 text="✨ Explore Publications" 31 url="https://leaflet.pub/discover" 32 /> 33 <HelpLink text="📣 Newsletter" url="https://buttondown.com/leaflet" /> 34 {/* contact links */} 35 <div className="columns-2 gap-2"> 36 <HelpLink 37 text="🦋 Bluesky" 38 url="https://bsky.app/profile/leaflet.pub" 39 /> 40 <HelpLink text="💌 Email" url="mailto:contact@leaflet.pub" /> 41 </div> 42 {/* keyboard shortcuts: desktop only */} 43 <Media mobile={false}> 44 {!props.noShortcuts && ( 45 <> 46 <hr className="text-border my-1" /> 47 <div className="flex flex-col gap-1"> 48 <Label>Text Shortcuts</Label> 49 <KeyboardShortcut name="Bold" keys={[metaKey(), "B"]} /> 50 <KeyboardShortcut name="Italic" keys={[metaKey(), "I"]} /> 51 <KeyboardShortcut name="Underline" keys={[metaKey(), "U"]} /> 52 <KeyboardShortcut 53 name="Highlight" 54 keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "H"]} 55 /> 56 <KeyboardShortcut 57 name="Strikethrough" 58 keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]} 59 /> 60 <KeyboardShortcut name="Inline Link" keys={[metaKey(), "K"]} /> 61 <KeyboardShortcut 62 name="Make Title" 63 keys={[metaKey(), isMac() ? "Opt" : "Alt", "1"]} 64 /> 65 <KeyboardShortcut 66 name="Make Heading" 67 keys={[metaKey(), isMac() ? "Opt" : "Alt", "2"]} 68 /> 69 <KeyboardShortcut 70 name="Make Subheading" 71 keys={[metaKey(), isMac() ? "Opt" : "Alt", "3"]} 72 /> 73 <KeyboardShortcut 74 name="Regular Text" 75 keys={[metaKey(), isMac() ? "Opt" : "Alt", "0"]} 76 /> 77 <KeyboardShortcut 78 name="Large Text" 79 keys={[metaKey(), isMac() ? "Opt" : "Alt", "+"]} 80 /> 81 <KeyboardShortcut 82 name="Small Text" 83 keys={[metaKey(), isMac() ? "Opt" : "Alt", "-"]} 84 /> 85 86 <Label>Block Shortcuts</Label> 87 {/* shift + up/down arrows (or click + drag): select multiple blocks */} 88 <KeyboardShortcut 89 name="Move Block Up" 90 keys={["Shift", metaKey(), "↑"]} 91 /> 92 <KeyboardShortcut 93 name="Move Block Down" 94 keys={["Shift", metaKey(), "↓"]} 95 /> 96 {/* cmd/ctrl-a: first selects all text in a block; again selects all blocks on page */} 97 {/* cmd/ctrl + up/down arrows: go to beginning / end of doc */} 98 99 <Label>Canvas Shortcuts</Label> 100 <OtherShortcut name="Add Block" description="Double click" /> 101 <OtherShortcut name="Select Block" description="Long press" /> 102 103 <Label>Outliner Shortcuts</Label> 104 <KeyboardShortcut 105 name="Make List" 106 keys={[metaKey(), isMac() ? "Opt" : "Alt", "L"]} 107 /> 108 {/* tab / shift + tab: indent / outdent */} 109 <KeyboardShortcut 110 name="Toggle Checkbox" 111 keys={[metaKey(), "Enter"]} 112 /> 113 <KeyboardShortcut 114 name="Toggle Fold" 115 keys={[metaKey(), "Shift", "Enter"]} 116 /> 117 <KeyboardShortcut 118 name="Fold All" 119 keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↑"]} 120 /> 121 <KeyboardShortcut 122 name="Unfold All" 123 keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↓"]} 124 /> 125 </div> 126 </> 127 )} 128 </Media> 129 {/* links: terms and privacy */} 130 <hr className="text-border my-1" /> 131 {/* <HelpLink 132 text="Terms and Privacy Policy" 133 url="https://leaflet.pub/legal" 134 /> */} 135 <div> 136 <a href="https://leaflet.pub/legal" target="_blank"> 137 Terms and Privacy Policy 138 </a> 139 </div> 140 </div> 141 </Popover> 142 ) : null; 143}; 144 145const KeyboardShortcut = (props: { name: string; keys: string[] }) => { 146 return ( 147 <div className="flex gap-2 justify-between items-center"> 148 {props.name} 149 <div className="flex gap-1 items-center font-bold"> 150 {props.keys.map((key, index) => { 151 return <ShortcutKey key={index}>{key}</ShortcutKey>; 152 })} 153 </div> 154 </div> 155 ); 156}; 157 158const OtherShortcut = (props: { name: string; description: string }) => { 159 return ( 160 <div className="flex justify-between items-center"> 161 <span>{props.name}</span> 162 <span> 163 <strong>{props.description}</strong> 164 </span> 165 </div> 166 ); 167}; 168 169const Label = (props: { children: React.ReactNode }) => { 170 return <div className="text-tertiary font-bold pt-2 ">{props.children}</div>; 171}; 172 173const HelpLink = (props: { url: string; text: string }) => { 174 const [isHovered, setIsHovered] = useState(false); 175 const handleMouseEnter = () => { 176 setIsHovered(true); 177 }; 178 const handleMouseLeave = () => { 179 setIsHovered(false); 180 }; 181 return ( 182 <a 183 href={props.url} 184 target="_blank" 185 className="py-2 px-2 rounded-md flex flex-col gap-1 bg-border-light hover:bg-border hover:no-underline" 186 style={{ 187 backgroundColor: isHovered 188 ? "rgb(var(--accent-light))" 189 : "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)", 190 }} 191 onMouseEnter={handleMouseEnter} 192 onMouseLeave={handleMouseLeave} 193 > 194 <strong>{props.text}</strong> 195 </a> 196 ); 197};